#include <stdlib.h>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"

////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////

int NoeudSeqInst::getValeur() {
    int valeur = 0;
    for (unsigned int i = 0; i < tabInst.size(); i++)
        valeur = tabInst[i]->getValeur(); // on evalue chaque instruction de la séquence
    return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
    for (unsigned int i = 0; i < tabInst.size(); i++)
        tabInst[i]->afficher(indentation + 1); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
    tabInst.push_back(instruction);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
    this->variable = variable;
    this->expression = expression;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudAffectation::getValeur() {
    int valeur = expression->getValeur(); // on évalue l'expression
    ((SymboleValue*) variable)->setValeur(valeur); // on affecte la variable
    return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////

void NoeudAffectation::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Affectation" << endl;
    variable->afficher(indentation + 1); // on affiche variable et expression
    expression->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurUnaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurUnaire::NoeudOperateurUnaire(Symbole operateur,
        Noeud* operandeDroit) {
    this->operateur = operateur;
    this->operandeDroit = operandeDroit;
}
////////////////////////////////////////////////////////////////////////////////

int NoeudOperateurUnaire::getValeur() {
    int valeur = 0;
    int od = operandeDroit->getValeur();
    if (this->operateur == "non") valeur = -od;
    return valeur;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurUnaire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Operateur Unaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
    operandeDroit->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire(Symbole operateur,
        Noeud* operandeGauche,
        Noeud* operandeDroit) {
    this->operateur = operateur;
    this->operandeGauche = operandeGauche;
    this->operandeDroit = operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudOperateurBinaire::getValeur() {
    int valeur = 0;
    int og = operandeGauche->getValeur();
    int od = operandeDroit->getValeur();
    if (this->operateur == "+") valeur = og + od;
    else if (this->operateur == "-") valeur = og - od;
    else if (this->operateur == "*") valeur = og * od;
    else if (this->operateur == "/")
        if (od != 0)
            valeur = og / od;
        else {
            cout << "Erreur pendant l'interpretation : division par zero" << endl;
            exit(0); // plus tard on levera une exception
        } else if (this->operateur == "et" || this->operateur == "&&") {
        valeur = od * og;
    } else if (this->operateur == "ou" || this->operateur == "||") {
        if (og == 1 || od == 1) {
            valeur = 1;
        } else {
            valeur = 0;
        }
    } else if (this->operateur == "==") {
        if (og == od) {
            valeur = 1;
        } else {
            valeur = 0;
        }
    } else if (this->operateur == "!=") {
        if (og != od) {
            valeur = 1;
        } else {
            valeur = 0;
        }
    } else if (this->operateur == "<") {
        if (og < od) {
            valeur = 1;
        } else {
            valeur = 0;
        }
    } else if (this->operateur == ">") {
        if (og > od) {
            valeur = 1;
        } else {
            valeur = 0;
        }
    } else if (this->operateur == "<=") {
        if (og <= od) {
            valeur = 1;
        } else {
            valeur = 0;
        }
    } else //if (this->operateur==">=")
    {
        if (og >= od) {
            valeur = 1;
        } else {
            valeur = 0;
        }
    }

    return valeur;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurBinaire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
    operandeGauche->afficher(indentation + 1); // on affiche fils gauche et fils droit
    operandeDroit->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstSi
////////////////////////////////////////////////////////////////////////////////

NoeudInstSi::NoeudInstSi() {
}

NoeudInstSi::NoeudInstSi(Noeud* s, Noeud* a, vector<Noeud*> sin) {
    si = s;
    alors = a;
    sinon = sin;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudInstSi::getValeur() {
    int valeur = 0;
    if (si->getValeur() == 1) {
        valeur = alors->getValeur();
    } else {
        for (unsigned int i = 0; i < sinon.size(); i++) {
            valeur = sinon[i]->getValeur();
        }
    }
    return valeur;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstSi::afficher(unsigned short indentation) {
	si->afficher(indentation);
	alors->afficher(indentation);
	for(unsigned int i = 0; i < sinon.size(); i++){
		sinon[i]->afficher(indentation);
	}

}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstTq
////////////////////////////////////////////////////////////////////////////////

NoeudInstTq::NoeudInstTq() {
}
////////////////////////////////////////////////////////////////////////////////

NoeudInstTq::NoeudInstTq(Noeud* cond, Noeud* seq) {
    m_iter = cond;
    m_inst = seq;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudInstTq::getValeur() {
    int valeur = 0;
    if (m_iter->getValeur() == 0) {
        valeur = m_iter->getValeur();
    } else {
        while (m_iter->getValeur() == 1) {
            valeur = this->m_inst->getValeur();
        }
    }
    return valeur;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstTq::afficher(unsigned short indentation) {
    this->m_iter->afficher(indentation);
    this->m_inst->afficher(indentation + 1);
}

void NoeudInstTq::ajouterIter(Noeud* iter) {
    this->m_iter = iter;
}

void NoeudInstTq::ajouterInst(Noeud* inst) {
    this->m_inst = inst;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstRepeter
////////////////////////////////////////////////////////////////////////////////

NoeudInstRepeter::NoeudInstRepeter() {

}

////////////////////////////////////////////////////////////////////////////////
NoeudInstRepeter::NoeudInstRepeter(Noeud*cond, Noeud* seq) {
	m_iter = cond;
	m_inst = seq;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstRepeter::getValeur() {
   int valeur=0;
   int valeur2=0;

   do{ valeur2 = m_inst->getValeur(); 
   valeur = m_iter->getValeur();
   cout << m_iter->getValeur()<< endl;}
   while (valeur == 0); 
   
   return valeur2;
}

void NoeudInstRepeter::afficher(unsigned short indentation) {
    this->m_inst->afficher(indentation);
	this->m_iter->afficher(indentation+1);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstPour
////////////////////////////////////////////////////////////////////////////////

NoeudInstPour::NoeudInstPour() {

}

////////////////////////////////////////////////////////////////////////////////
NoeudInstPour::NoeudInstPour(Noeud* i, Noeud* c, Noeud* it, Noeud* s) {
	this->init = i;
	this->cond = c;
	this->iter = it;
	this->seq = s;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstPour::getValeur() {
    int valeur = 0;
	valeur=this->init->getValeur();
	while(this->cond->getValeur()==1){
		valeur=this->iter->getValeur();
		valeur=this->seq->getValeur();
	}
    return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstPour::afficher(unsigned short indentation) {
    this->init->afficher(indentation);
	this->cond->afficher(indentation+1);
	this->iter->afficher(indentation+2);
	this->seq->afficher(indentation+3);
}


////////////////////////////////////////////////////////////////////////////////
// NoeudInstLire
////////////////////////////////////////////////////////////////////////////////

NoeudInstLire::NoeudInstLire() {
    val = NULL;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudInstLire::getValeur() {
    return this->val->getValeur();
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstLire::afficher(unsigned short indentation) {
    this->val->afficher(indentation);
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstLire::ajouterSymboleValue(Noeud* val) {
    this->val = val;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstEcrire
////////////////////////////////////////////////////////////////////////////////

NoeudInstEcrire::NoeudInstEcrire() {

}

////////////////////////////////////////////////////////////////////////////////

int NoeudInstEcrire::getValeur() {
    return val->getValeur();
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstEcrire::afficher(unsigned short indentation) {
    val->afficher(indentation);
}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstEcrire::ajouterSymboleValue(Noeud* val) {
    this->val = val;
}

////////////////////////////////////////////////////////////////////////////////
NoeudSwitch::NoeudSwitch(vector<Noeud*> ent, vector<Noeud*> seq, Noeud* var) :
entiers(ent),
sequence(seq),
variable(var) {
}

int NoeudSwitch::getValeur() {
	bool passe = false;
	unsigned int i = 0;

	while (i < sequence.size() and !passe) {
		if (i < entiers.size() and entiers[i]->getValeur() == variable->getValeur()) {
			sequence[i]->getValeur();
			passe = true;
		} else if (sequence.size() > entiers.size() and i >= entiers.size())
			sequence[i]->getValeur();
		i++;
	}

	return 0;
}

void NoeudSwitch::afficher(unsigned short indentation) {
	Noeud::afficher(indentation);
	cout << "Noeud - Selon" << endl;
	unsigned int i = 0;
	while (i < entiers.size()) {
		entiers[i]->afficher(indentation + 1);
		sequence[i]->afficher(indentation + 2);
		i++;
	}
	if (sequence.size() > entiers.size()) {
		Noeud::afficher(indentation + 1);
		cout << "Feuille - Cas Defaut" << endl;
		sequence[i]->afficher(indentation + 2);
	}

	variable->afficher(indentation + 1);
}